package com.leyou.trade.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.leyou.auth.constants.JwtConstants;
import com.leyou.auth.utils.UserContext;
import com.leyou.common.exception.LyException;
import com.leyou.common.utils.CookieUtils;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.SkuDTO;
import com.leyou.trade.constants.PayConstants;
import com.leyou.trade.dto.OrderDTO;
import com.leyou.trade.dto.OrderFormDTO;
import com.leyou.trade.entity.Order;
import com.leyou.trade.entity.OrderDetail;
import com.leyou.trade.entity.OrderLogistics;
import com.leyou.trade.entity.enums.OrderStatus;
import com.leyou.trade.mapper.OrderMapper;
import com.leyou.trade.service.OrderDetailService;
import com.leyou.trade.service.OrderLogisticsService;
import com.leyou.trade.service.OrderService;
import com.leyou.trade.utils.CookieContext;
import com.leyou.trade.utils.PayHelper;
import com.leyou.user.client.UserClient;
import com.leyou.user.dto.AddressDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

import static com.leyou.common.constants.MQConstants.ExchangeConstants.ORDER_EXCHANGE_NAME;
import static com.leyou.common.constants.MQConstants.RoutingKeyConstants.EVICT_ORDER_KEY;
import static com.leyou.trade.constants.PayConstants.ORDER_NO_KEY;
import static com.leyou.trade.constants.PayConstants.TOTAL_FEE_KEY;

@Service
@Slf4j
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {

    @Autowired
    private ItemClient itemClient;

    @Autowired
    private OrderDetailService detailService;

    @Autowired
    private UserClient userClient;

    @Autowired
    private OrderLogisticsService logisticsService;


    @Autowired
    private AmqpTemplate amqpTemplate;

    @Transactional
    @Override
    public Long createOrder(OrderFormDTO orderFormDTO, HttpServletRequest request) {

        //从请求中获取token，存入CookieContext中
        try {
            //获取sku的map集合,key为sku的id，value为sku对应的值，num
            Map<Long, Integer> carts = orderFormDTO.getCarts();

            //减库存，加销量业务,业务出错了，
            this.itemClient.deductStock(carts);

            String token = CookieUtils.getCookieValue(request, JwtConstants.COOKIE_NAME);

            CookieContext.setCookie(token);


            Order order = new Order();

            order.setUserId(UserContext.getUser().getId());


            //根据sku的id查询对应的sku集合
            List<SkuDTO> skuDTOS = this.itemClient.querySkuByIds(carts.keySet().stream().collect(Collectors.toList()));

            //totalFee   count(sku.price*num)
            long totalFee = 0;
            for (SkuDTO skuDTO : skuDTOS) {
                totalFee += skuDTO.getPrice() * carts.get(skuDTO.getId());
            }
            //
            order.setTotalFee(totalFee);//总价
            order.setActualFee(totalFee);//实际支付的价格
            order.setPaymentType(orderFormDTO.getPaymentType()); //支付方式
            order.setPostFee(0L);//运费
            order.setStatus(OrderStatus.INIT); //保存到数据库中其实是1，

            //保存order并且主键回显，判断保存是否成功,
            if (!save(order)) {
                throw new LyException(500, "订单保存失败");
            }

            //orderDetail

            List<OrderDetail> orderDetails = new ArrayList<>();
            for (SkuDTO skuDTO : skuDTOS) {
                OrderDetail orderDetail = new OrderDetail();

                BeanUtils.copyProperties(skuDTO, orderDetail);

                orderDetail.setSkuId(skuDTO.getId());
                //skuDto的id不能作为orderDetail的id
                orderDetail.setId(null);
                orderDetail.setImage(StringUtils.substringBefore(skuDTO.getImages(), ","));
                orderDetail.setSpec(skuDTO.getSpecialSpec());
                orderDetail.setNum(carts.get(skuDTO.getId()));

                orderDetail.setOrderId(order.getOrderId());


                orderDetails.add(orderDetail);
            }

            if (!detailService.saveBatch(orderDetails)) {
                throw new LyException(500, "订单详情保存失败");
            }

            //保存地址信息logistics
            //根据用户收件地址的id，查询用户的实际收货地址

            AddressDTO addressDTO = this.userClient.queryAddressById(orderFormDTO.getAddressId());

            OrderLogistics logistics = new OrderLogistics();

            BeanUtils.copyProperties(addressDTO, logistics);
            logistics.setOrderId(order.getOrderId());

            //保存物流信息
            if (!this.logisticsService.save(logistics)) {
                throw new LyException(500, "订单物流信息保存失败");
            }
            // throw new RuntimeException("故意捣乱");

            //订单创建成功，向mq发送一个消息，对当前订单产生 定时 监控
            amqpTemplate.convertAndSend(ORDER_EXCHANGE_NAME, EVICT_ORDER_KEY, order.getOrderId());
            return order.getOrderId();
        } catch (LyException e) {
            throw new LyException(e.getStatus(), e.getMessage());
        } catch (BeansException e) {
            throw new LyException(500, "类型转换失败");
        }
    }

    @Override
    public OrderDTO queryOrderById(Long orderId) {
        return new OrderDTO(getById(orderId));
    }


    @Autowired
    private PayHelper payHelper;

    @Override
    public String getPayUrl(Long orderId) {

        Long actualFee = getById(orderId).getActualFee();

        //TODO 地址有2小时有效期，同一个订单，2小时内没必要重复生成支付链接，redis缓存，2H

        return this.payHelper.getPayUrl(orderId, 1L/*actualFee*/, PayConstants.ORDER_DESC);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handleNotify(Map<String, String> data) {

        /**
         * 返回的内容过多
         * 1，校验签名，
         * 2，核实订单，要付的钱和收到的钱是否一致
         * 3，查验订单是否已经支付过
         */
        // 1.业务标示校验
        payHelper.checkResultCode(data);
        // 2.签名校验
        payHelper.checkResponseSignature(data);

        // 3.订单状态校验（保证幂等，防止重复通知）
        String outTradeNo = data.get(ORDER_NO_KEY);
        String totalFee = data.get(TOTAL_FEE_KEY);
        if (StringUtils.isBlank(outTradeNo) || StringUtils.isBlank(totalFee)) {
            // 数据有误
            throw new RuntimeException("响应数据有误，订单金额或编号为空！");
        }
        Long orderId = Long.valueOf(outTradeNo);
        Order order = getById(orderId);
        if (!order.getStatus().equals(OrderStatus.INIT.getValue())) {
            // 说明订单已经支付过了，属于重复通知，直接返回
            return;
        }

        // 4.订单金额校验
        Long total = Long.valueOf(totalFee);
        if (!total.equals(1L/*order.getActualFee()*/)) {
            throw new RuntimeException("订单金额有误，我要报警了！");
        }

        /**
         * 没有处理过要修改订单状态
         * update tb_order set status = 2,set pay_time = now() where order_id = #{orderId}
         */


        update()
                .set("status", OrderStatus.PAY_UP.getValue())
                .set("pay_time", new Date())
                .eq("order_id", orderId)
                .update();


        log.info("处理微信支付通知成功！{}", data);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void clearOrder() {

        Map<Long, Integer> plusMap = new HashMap<>();

        //查询所有的状态为1的订单
        List<Order> orders = query()
                .eq("status", 1)
                .list();

        for (Order order : orders) {
            //判断是否过期
            if (new DateTime(order.getCreateTime()).plusHours(24).isBeforeNow()) {

                //update set status = 5 where order_id = xxx
                //关闭当前订单,关闭的条件为status为1，并且order_id为指定id
                boolean result = update().set("status", OrderStatus.CLOSED.getValue())
                        .eq("order_id", order.getOrderId())
                        .eq("status", OrderStatus.INIT.getValue())
                        .update();

                //如果有订单可关，才去加库存
                if (result) {
                    //查询到当前订单下包含的所有的商品，
                    List<OrderDetail> orderDetails = detailService.query().eq("order_id", order.getOrderId()).list();

                    //遍历订单详情，查验，其中商品信息，加入到plusMap
                    orderDetails.forEach(orderDetail -> {
                        Long skuId = orderDetail.getSkuId();
                        Integer num = orderDetail.getNum();

                        //说明本次批量处理的订单中已经有了对应的商品，则，直接修改其数量
                        if (plusMap.containsKey(skuId)) {
                            //合并数量累积
                            plusMap.put(skuId, plusMap.get(skuId) + num);
                        } else {
                            plusMap.put(skuId, num);
                        }

                    });

                }

            }
        }

        //TODO 如果plusMap中为空，没有必要发起web请求
        this.itemClient.addStock(plusMap);
    }

    @Override
    public void evictOrderIfNecessary(Long orderId) {

        //select * from tb_order where order_id = #{orderId} and status = 1
        Order order = query().eq("order_id", orderId)
                .eq("status", OrderStatus.INIT.getValue())
                .one();

        if (null!=order){

            Order od = new Order();
            od.setOrderId(orderId);


            //update set status = 5 where order_id = xxx
            //关闭当前订单,关闭的条件为status为1，并且order_id为指定id
            boolean result = update().set("status", OrderStatus.CLOSED.getValue())
                    .eq("order_id", order.getOrderId())
                    .eq("status", OrderStatus.INIT.getValue())
                    .update();

            //如果有订单可关，才去加库存
            if (result) {

                //查询到当前订单下包含的所有的商品，
                List<OrderDetail> orderDetails = detailService.query().eq("order_id", order.getOrderId()).list();


                Map<Long,Integer> plusCartMap = new HashMap<>();
                //遍历订单详情，查验，其中商品信息，加入到plusMap
                orderDetails.forEach(orderDetail -> {
                    Long skuId = orderDetail.getSkuId();
                    Integer num = orderDetail.getNum();

                    plusCartMap.put(skuId,num);
                });

                //获取到当前订单中所有的商品了，直接执行加库存逻辑
                this.itemClient.addStock(plusCartMap);

            }
        }
    }

    @Override
    public Integer queryPayStatus(Long orderId) {
        Order order = getById(orderId);
        if(null == orderId){
            throw  new LyException(404,"订单不存在");
        }
        return order.getStatus().getValue();
    }


    @Override
    public void UpdateStatus(Long skuId) {

        OrderDetail orderDetail = detailService.query().eq("sku_id", skuId).one();

        boolean result = update().set("status", OrderStatus.CONFIRMED.getValue())
                .eq("order_id", orderDetail.getId())
                .update();
        if(!result){
            throw  new  LyException(500,"修改订单状态失败");
        }
    }
}
